#!/usr/bin/perl

#
# Copyright (c) 2008 James Molloy, James Pritchett, Jörg Pfähler, Matthew Iselin
#
# Permission to use, copy, modify, and distribute this software for any
# purpose with or without fee is hereby granted, provided that the above
# copyright notice and this permission notice appear in all copies.
#
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
#

# Parses a "buildFlags" file.
# Format is:
#   Lines beginning with a # are comments.
#   All other lines are build rules - "LEFT: RIGHT1 RIGHT2 ..." - if left is defined, then define right1 and right2...
#   If a build rule is encapsulated in [], e.g. "[LEFT]:", then this variable is internal and will not be
#   displayed in the GUI.
#   If a build rule is encapsulated in {}, e.g. "{LEFT}:", then this variable will be marked as 'advanced'.
#   '*' at the start of a line denotes a flag which is enabled by default.

use strict;
use warnings;

open my $r, "<", "buildFlags" or die("Couldn't open file 'buildFlags' for reading.");
open my $w, ">", "buildFlags.cmake" or die("Couldn't open file 'buildFlags.cmake' for writing.");

my %rule_rhs;
my %rule_isadvanced;
my %rule_isinternal;
my %rule_isdefault;
my %archs;
my %tests;

sub is_internal {
  my ($rule) = @_;
  return 1 if ($rule_isinternal{$rule} and ($rule_isinternal{$rule} == 1));
  return 0 if ($rule_rhs{$_[0]});
  return 1;
}

sub get_all {
  my %all = %rule_rhs;
  foreach (keys %rule_rhs) {
    foreach (split (/ +/, $rule_rhs{$_})) {
      $all{$_} = 1;
    }
  }
  return keys %all;
}

while (<$r>) {
  next if $_ =~ m/^#/; # Detect comments.
  next if $_ =~ m/^[ \t]*$/; # Detect empty lines.
  
  my $default = 0;
  my $line  = $_;
  if ($_ =~ m/^\* *(.+)$/) {
    $line = $1;
    $default = 1;
  }
  
  my $right = '';
  my $left = '';
  my $internal = 0;
  my $advanced = 0;
  if ($line =~ m/^(\w+): ?(.*)$/) {
    $left = $1;
    $right = $2;
  }
  elsif ($line =~ m/^\[(\w+)\]: ?(.*)$/) {
    $internal = 1;
    $left = $1;
    $right = $2;
  }
  elsif ($line =~ m/^\{(\w+)\}: ?(.*)$/) {
    $advanced = 1;
    $left = $1;
    $right = $2;
  }
  elsif ($line =~ m/^\(([\w-]+)\): ?(.*)$/) {
    $archs{$1} = $2;
    next;
  }
  elsif ($line =~ m/^`(\w+)`: ?(.*)$/) {
    $tests{$1} = $2 || '';
    next;
  }
  
  $rule_rhs{$left} = $right || " ";
  $rule_isadvanced{$left} = $advanced;
  $rule_isinternal{$left} = $internal;
  $rule_isdefault{$left} = $default;
}

# Start of output.
print $w "#\n";
print $w "# Copyright (c) 2008 James Molloy, James Pritchett, Jörg Pfähler, Matthew Iselin\n";
print $w "#\n";
print $w "# Permission to use, copy, modify, and distribute this software for any\n";
print $w "# purpose with or without fee is hereby granted, provided that the above\n";
print $w "# copyright notice and this permission notice appear in all copies.\n";
print $w "#\n";
print $w "# THE SOFTWARE IS PROVIDED \"AS IS\" AND THE AUTHOR DISCLAIMS ALL WARRANTIES\n";
print $w "# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF\n";
print $w "# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR\n";
print $w "# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES\n";
print $w "# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN\n";
print $w "# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF\n";
print $w "# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.\n";
print $w "#\n";
print $w "\n";
print $w "#\n";
print $w "# AUTOGENERATED BY 'scripts/parseBuildFlags.pl' FROM 'buildFlags' -- DO NOT EDIT!\n";
print $w "#                                                                    ===========\n";
print $w "\n";

print $w "# Define rules initially.\n";
print $w "if(NOT DEFINED RULES_DEFINED)\n";
print $w "  set(RULES_DEFINED YES CACHE INTERNAL \"Rules defined.\" FORCE)\n";
foreach my $rule (get_all()) {
my $on = "OFF";
$on = "ON" if (defined $rule_isdefault{$rule} and $rule_isdefault{$rule} == 1);
if (is_internal($rule)) {
print $w "  set($rule $on CACHE INTERNAL \"$rule\")\n";
} else {
print $w "  set($rule $on CACHE BOOL \"$rule\")\n";
}
if (defined $rule_isadvanced{$rule} and $rule_isadvanced{$rule} == 1) {
print $w "  mark_as_advanced(FORCE $rule)\n";
}
}
print $w "endif(NOT DEFINED RULES_DEFINED)\n";
print $w "\n";

print $w "# Loop five times - hopefully then no more variables will be being changed.\n";
print $w "foreach(CHANGED RANGE 5)\n";
print $w "\n";

foreach my $rule (keys %rule_rhs) {
print $w "  # Rule: $rule.\n";
print $w "  if($rule)\n";
print $w "    set($rule-PREV ON CACHE INTERNAL \"\" FORCE)\n";
print $w "    set(DEFINES \${DEFINES} $rule)\n";
print $w "    # Set flags $rule_rhs{$rule}.\n";
foreach (split (/ +/, $rule_rhs{$rule})) {
if (is_internal($_)) {
print $w "    set($_ ON CACHE INTERNAL \"$_\" FORCE)\n";
} else {
print $w "    set($_ ON CACHE BOOL \"$_\" FORCE)\n";
}
print $w "    set(DEFINES \${DEFINES} $_)\n";
}
print $w "  else($rule)\n";
print $w "    if($rule-PREV STREQUAL ON)\n";
print $w "      set($rule-PREV OFF CACHE INTERNAL \"\" FORCE)\n";
foreach (split (/ +/, $rule_rhs{$rule})) {
if (is_internal($_)) {
print $w "      set($_ OFF CACHE INTERNAL \"$_\" FORCE)\n";
} else {
# print $w "      set($_ OFF CACHE BOOL  \"$_\" FORCE)\n";
}
print $w "      list(REMOVE_ITEM DEFINES $_)\n";
}
print $w "    endif($rule-PREV STREQUAL ON)\n";
print $w "  endif($rule)\n";
}

print $w "\n";
print $w "endforeach(CHANGED RANGE 5)\n";

print $w "\n";
print $w "list(REMOVE_DUPLICATES DEFINES)\n";

print $w "set(EMUL invalid)\n";
print $w "if(X86_COMMON)\n";
print $w "  set(EMUL qemu)\n";
print $w "endif(X86_COMMON)\n";
print $w "if(MIPS_COMMON)\n";
print $w "  set(EMUL qemu-mipsel)\n";
print $w "endif(MIPS_COMMON)\n";
print $w "if(ARM_COMMON)\n";
print $w "  set(EMUL qemu-arm)\n";
print $w "endif(ARM_COMMON)\n";

foreach my $test (keys %tests) {
print $w "if(";
foreach (split(/ +/, $tests{$test})) {
print $w "$_ OR ";
}
print $w "1)\n";

print $w "  add_test($test ../tests/$test.exp \${EMUL})\n";
print $w "endif(";
foreach (split(/ +/, $tests{$test})) {
print $w "$_ OR ";
}
print $w "1)\n";
}
close $r;
close $w;

# Write a test file out.
# For every architecture, make a new file in ./tests.
foreach my $arch (keys %archs) {

my %defines;
foreach (split / +/, $archs{$arch}) {
  $defines{$_} = 1;
}
my $changed = 1;
while ($changed) {
  $changed = 0;
  my $oldlength = scalar keys %defines;
  foreach my $rule (keys %defines) {
    if ($rule_rhs{$rule} and length $rule_rhs{$rule}) {
      foreach (split(/ +/, $rule_rhs{$rule})) {
        $defines{$_} = 1;
      }
    }
}
  $changed = 1 if ($oldlength != (scalar keys %defines));
}

open my $w, ">", "./tests/$arch.cmake";
print $w "#\n";
print $w "# Copyright (c) 2008 James Molloy, James Pritchett, Jörg Pfähler, Matthew Iselin\n";
print $w "#\n";
print $w "# Permission to use, copy, modify, and distribute this software for any\n";
print $w "# purpose with or without fee is hereby granted, provided that the above\n";
print $w "# copyright notice and this permission notice appear in all copies.\n";
print $w "#\n";
print $w "# THE SOFTWARE IS PROVIDED \"AS IS\" AND THE AUTHOR DISCLAIMS ALL WARRANTIES\n";
print $w "# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF\n";
print $w "# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR\n";
print $w "# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES\n";
print $w "# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN\n";
print $w "# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF\n";
print $w "# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.\n";
print $w "#\n";
print $w "\n";
print $w "#\n";
print $w "# AUTOGENERATED BY 'scripts/parseBuildFlags.pl' FROM 'buildFlags' -- DO NOT EDIT!\n";
print $w "#                                                                    ===========\n";
print $w "\n";

# This is a script to set up CTest for running tests on $arch.
print $w "set($arch ON CACHE BOOL \"$arch\" FORCE)\n";
foreach (keys %rule_rhs) {
print $w "set($_ OFF CACHE BOOL \"$_\" FORCE)\n";
}
foreach (keys %defines) {
print $w "set($_ ON CACHE BOOL \"$_\" FORCE)\n";
}
}

close $w;

# Generate the nightly test script.
open $w, ">", "scripts/runNightlyTest.pl";
print $w "#!/usr/bin/perl\n";
print $w "\n";
print $w "#\n";
print $w "# Copyright (c) 2008 James Molloy, James Pritchett, Jörg Pfähler, Matthew Iselin\n";
print $w "#\n";
print $w "# Permission to use, copy, modify, and distribute this software for any\n";
print $w "# purpose with or without fee is hereby granted, provided that the above\n";
print $w "# copyright notice and this permission notice appear in all copies.\n";
print $w "#\n";
print $w "# THE SOFTWARE IS PROVIDED \"AS IS\" AND THE AUTHOR DISCLAIMS ALL WARRANTIES\n";
print $w "# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF\n";
print $w "# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR\n";
print $w "# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES\n";
print $w "# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN\n";
print $w "# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF\n";
print $w "# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.\n";
print $w "#\n";
print $w "\n";
print $w "#\n";
print $w "# AUTOGENERATED BY 'scripts/parseBuildFlags.pl' FROM 'buildFlags' -- DO NOT EDIT!\n";
print $w "#                                                                    ===========\n";
print $w "\n";
foreach my $arch (keys %archs) {
print $w "print `cd build/ && rm -rf *`;\n";
print $w "print `cd build/ && cmake -C ../tests/$arch.cmake .. 2>&1`;\n";
print $w "print `cd build/ && ctest -D Nightly`;\n";
}
close $w;
`chmod +x scripts/runNightlyTest.pl`;
